﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;
using StyleMVVM.LanguageExtensions;

namespace StyleMVVM.Validation.Impl.Method
{
	public class ValidationMethodDefinition<T> : IValidationMethodDefinition<T>
	{
		private readonly Action<IRuleExecutionContext<T>> action;
		private readonly Func<IRuleExecutionContext<T>, Task> func;
		private readonly List<string> standards = new List<string>();
		private string name;
		private readonly List<string> properties = new List<string>();
		private readonly IReflectionService reflectionService;

		public ValidationMethodDefinition(IReflectionService reflectionService,
		                                  Action<IRuleExecutionContext<T>> action,
		                                  Func<IRuleExecutionContext<T>, Task> func)
		{
			this.reflectionService = reflectionService;

			this.action = action;
			this.func = func;
		}

		public IValidationMethodDefinition<T> MonitorProperty<TProp>(Expression<Func<T, TProp>> propertyExpression)
		{
			string propertyName = reflectionService.GetPropertyAccessPath(propertyExpression);

			properties.Add(propertyName);

			return this;
		}

		public IValidationMethodDefinition<T> Standard(string standard)
		{
			standards.Add(standard);

			return this;
		}

		public IValidationMethodDefinition<T> Name(string name)
		{
			if (!string.IsNullOrEmpty(this.name))
			{
				throw new Exception("You can only provide one name for a rule: " + name);
			}

			this.name = name;

			return this;
		}

		public IValidationRule ProvideRule()
		{
			if (action != null)
			{
				if (string.IsNullOrEmpty(name))
				{
					name = action.GetMethodInfo().Name;
				}

				return new SyncValidationMethodRule<T>(action, name, standards, properties);
			}

			if (func != null)
			{
				if (string.IsNullOrEmpty(name))
				{
					name = func.GetMethodInfo().Name;
				}

				return new AsyncValidationMethodRule<T>(func, name, standards, properties);
			}

			return null;
		}
	}
}